package models

import (
	"reflect"
	"strings"
)

/**
	orm框架
	实体与mysql数据库交互语句有
	SELECT attribute FROM tableName where primarykey = ?
	INSERT INTO tableName( attributes ) VALUES (?,?,?)
    DELETE FROM tableName where primaryKey=?
	UPDATE tableName set attribute = ?
*/

func getTableName(entity interface{}) string {
	return ToDatabaseName(reflect.TypeOf(entity).Name())
}

func GetFieldName(entity interface{}, filter func(interface{}) bool) []string {
	ans := make([]string, 0)
	typeField := reflect.TypeOf(entity)
	for i := 0; i < typeField.NumField(); i++ {
		f := typeField.Field(i)
		value := f.Tag.Get("ignore")
		if value == "true" {
			continue
		}
		if filter(f) {
			continue
		}
		ans = append(ans, ToDatabaseName(f.Name))
	}
	return ans
}
func GetFieldValue(entity interface{}, filter func(interface{}) bool) []interface{} {
	ans := make([]interface{}, 0)
	typeField := reflect.TypeOf(entity)
	valueField := reflect.ValueOf(entity)
	for i := 0; i < typeField.NumField(); i++ {
		f := typeField.Field(i)
		value := f.Tag.Get("ignore")
		if value == "true" {
			continue
		}
		if filter(f) {
			continue
		}
		v := valueField.Field(i)
		ans = append(ans, v.Interface())
	}
	return ans
}
func getFiledWithParamString(entity interface{}, filter func(interface{}) bool, sep string) string {
	fileds := GetFieldName(entity, filter)
	for i, _ := range fileds {
		fileds[i] = fileds[i] + " = ?"
	}
	return strings.Join(fileds, sep)
}

func GetFieldString(table interface{}, fiter func(interface{}) bool) string {
	fileds := GetFieldName(table, fiter)
	return strings.Join(fileds, ",")
}

func ToDatabaseName(name string) string {
	ans := make([]rune, 0)
	for i, v := range name {
		if i == 0 {
			ans = append(ans, v)
		} else {
			if v >= 'A' && v <= 'Z' {
				ans = append(ans, '_')
				ans = append(ans, v)
			} else {
				ans = append(ans, v)
			}
		}
	}
	return strings.ToLower(string(ans))
}

func IsPrimaryKey(f interface{}) bool {
	v := f.(reflect.StructField)
	tag := v.Tag.Get("pk")
	if strings.Contains(tag, "ai") {
		return true
	} else {
		return false
	}
}

func IsNotPrimaryKey(f interface{}) bool {
	return !IsPrimaryKey(f)
}

func IsNothing(f interface{}) bool {
	return false
}

func QueryByPrimaryKey(entity interface{}) string {
	tableName := getTableName(entity)
	filedString := GetFieldString(entity, IsNothing)
	sql := "SELECT " + filedString + " FROM  " + tableName + WhereWithPrimaryKey(entity)
	return sql
}
func QueryByEqualField(entity interface{}, args ...string) string {
	tableName := getTableName(entity)
	filedString := GetFieldString(entity, IsNothing)
	sql := "SELECT " + filedString + " FROM  " + tableName + WhereWithArgsSql(args)
	return sql
}
func QueryByNotEqualField(entity interface{}, args ...string) string {
	tableName := getTableName(entity)
	filedString := GetFieldString(entity, IsNothing)
	sql := "SELECT " + filedString + " FROM  " + tableName + WhereWithAllNotEqual(args)
	return sql
}
func UpdateFieldsByPrimary(entity interface{}, setArgs ...string) string {
	tableName := getTableName(entity)
	field := SetArgsSql(setArgs)
	sql := "UPDATE " + tableName + " SET " + field + " " + WhereWithPrimaryKey(entity)
	return sql
}
func UpdateByPrimaryKey(entity interface{}) string {
	tableName := getTableName(entity)
	fieldParamString := getFiledWithParamString(entity, IsPrimaryKey, " , ")
	sql := "UPDATE " + tableName + " SET  " + fieldParamString + WhereWithPrimaryKey(entity)
	return sql
}
func DeleteByPrimaryKey(entity interface{}) string {
	tableName := getTableName(entity)
	return "DELETE FROM " + tableName + WhereWithPrimaryKey(entity)
}
func DeleteByField(entity interface{}, args ...string) string {
	tableName := getTableName(entity)
	return "DELETE FROM " + tableName + WhereWithArgsSql(args)
}
func InsertWithoutPrimaryKey(entity interface{}) string {
	tableName := getTableName(entity)
	s := GetFieldString(entity, IsPrimaryKey)
	return "INSERT INTO " + tableName +
		"(" + s + ")" +
		" VALUES" +
		"(" + getParamByCount(len(GetFieldName(entity, IsPrimaryKey))) + ")"

}
func InsertIntoTable(entity interface{}) string {
	tableName := getTableName(entity)
	s := GetFieldString(entity, IsNothing)
	return "INSERT INTO " + tableName +
		"(" + s + ")" +
		" VALUES" +
		"(" + getParamByCount(len(GetFieldName(entity, IsNothing))) + ")"
}
func getParamByCount(count int) string {
	var ans string
	for i := 0; i < count; i++ {
		if i == 0 {
			ans = "?"
		} else {
			ans = ans + ",?"
		}
	}
	return ans
}

func SetArgsSql(args []string) string {
	if args == nil || len(args) == 0 {
		return ""
	}
	for i, _ := range args {
		args[i] = ToDatabaseName(args[i]) + " = ? "
	}
	return strings.Join(args, ",")
}

func WhereWithArgsSql(args []string) string {
	if args == nil || len(args) == 0 {
		return ""
	}
	for i, _ := range args {
		args[i] = ToDatabaseName(args[i]) + " = ? "
	}
	return " WHERE " + strings.Join(args, " AND ")
}
func WhereWithPrimaryKey(entity interface{}) string {
	sql := " WHERE "
	primaryParamString := getFiledWithParamString(entity, IsNotPrimaryKey, " and ")
	return sql + primaryParamString
}

func WhereWithAllNotEqual(args []string) string {
	if args == nil || len(args) == 0 {
		return ""
	}
	for i, _ := range args {
		args[i] = ToDatabaseName(args[i]) + " != ? "
	}
	return " WHERE " + strings.Join(args, " AND ")
}

func WhereWithArgs(args []string, condition []string, logicConnect []string) string {
	if args == nil || len(args) == 0 {
		return ""
	}
	sql := args[0] + " " + condition[0] + " ?"
	for i, _ := range args {
		if i == 0 {
			continue
		}
		sql = sql + "  " + logicConnect[i-1] + " " + args[i] + " " + condition[0] + " ? "
	}
	return sql
}
